#include <boost/lexical_cast.hpp>

#include "scene/scenemanager.h"
#include "scene/entity.h"
#include "map/q3bsp.h"
#include "scene/scenenode.h"
#include "scene/octree.h"
#include "core/logger.h"
#include "scene/skybox.h"
#include "scene/camera.h"

namespace engine {

	namespace scene {
		
		using engine::map::Q3BSP;
		using engine::map::WorldMesh;
		
		SceneManager::SceneManager() {
			m_RootSceneNode = shared_ptr<SceneNode>(new SceneNode());
			m_TextureManager = shared_ptr<TextureManager> (new TextureManager());
			m_MaterialManager = shared_ptr<MaterialManager> (new MaterialManager());
			m_MaterialManager->setTextureManager(m_TextureManager);
			
			Logger::getLogger("scenemanager")->setLevel(LL_DEBUG);
		}
		
		SceneManager::~SceneManager() {
			
		}
				
		///	Creates a new entity for the scene, must be attached to a scene node to be
		/// renderered
		shared_ptr<Entity> SceneManager::createEntity(const string& name, const string& mesh) {
			assert(m_Objects.find(name) == m_Objects.end());
			
			shared_ptr<Entity> ent(new Entity(name));
			ent->useMesh(mesh);
			m_Objects[name] = ent;
			
			ent->setTextureManager(m_TextureManager);
			ent->setMaterialManager(m_MaterialManager);
			return ent;
		}
				
		void SceneManager::setAmbientLight(const slVec4& colour) {
			
		}
		
		void SceneManager::setSkyBox(bool enable, const string& texturePath) {
			Logger::getLogger("scenemanager")->debug("Loading skybox");
			shared_ptr<Skybox> sky(new Skybox());
			
			sky->setTextureManager(m_TextureManager);
			
			if (!sky->loadSkybox("textures/sky")) {
				Logger::getLogger("scenemanager")->error("Skybox loading failed!");
				return;
			}
			
			shared_ptr<SceneNode> node = m_RootSceneNode->createChildSceneNode("sky");
			node->attachObject(sky);
			
			Logger::getLogger("scenemanager")->debug("Rootsize: " + boost::lexical_cast<string>(m_RootSceneNode->getNumChildNodes()));
		}
		
		shared_ptr<SceneNode> SceneManager::getRootSceneNode() {
			return m_RootSceneNode;
		}
		
		bool SceneManager::setWorldGeometry(const string& filename) {
			
			shared_ptr<WorldMesh> worldGeometry = Q3BSP::create("geometry");
			worldGeometry->setTextureManager(m_TextureManager);
			worldGeometry->setMaterialManager(m_MaterialManager);
			m_Octree = shared_ptr<Octree> ( new Octree() );
			
			
			if (!worldGeometry->loadMap(filename)) {
				return false;
			}
			
			m_Octree->buildTree(*worldGeometry.get());
			shared_ptr<SceneNode> node = m_RootSceneNode->createChildSceneNode("geometry");
			
			node->attachObject(worldGeometry);
			node->translate(slVec3(0.0, 0.0, -500));
			return true;			
		}
		
		shared_ptr<Camera> SceneManager::createCamera(const string& name) {
			if (m_Cameras.find(name) != m_Cameras.end()) {
				Logger::getLogger("scenemanager")->warning("There is already a camera with name: " + name + ". Returning that one instead of creating a new");
				return m_Cameras[name];
			}
			
			m_Cameras.insert(make_pair(name, shared_ptr<Camera> (new Camera(name))));
			return m_Cameras[name];
		}
		
		shared_ptr<Camera> SceneManager::getCamera(const string& name) {
			assert(m_Cameras.find(name) != m_Cameras.end());
			return m_Cameras[name];
		}
		
		bool SceneManager::setActiveCamera(const string& name) {
			if (m_Cameras.find(name) == m_Cameras.end()) {
				return false;
			}
			
			m_ActiveCamera = m_Cameras[name];
			return true;
		}
		
		shared_ptr<Camera> SceneManager::getActiveCamera() {
			return m_ActiveCamera;
		}
	}
}
